package cn.kgc.springbootdemo.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.kgc.springbootdemo.constant.CommonConstants;
import cn.kgc.springbootdemo.entity.RoleMenu;
import cn.kgc.springbootdemo.entity.User;
import cn.kgc.springbootdemo.entity.UserLoginRecord;
import cn.kgc.springbootdemo.entity.UserRole;
import cn.kgc.springbootdemo.form.LoginForm;
import cn.kgc.springbootdemo.form.RegisterUserForm;
import cn.kgc.springbootdemo.form.UpdateUserForm;
import cn.kgc.springbootdemo.form.UserPageForm;
import cn.kgc.springbootdemo.mapper.UserMapper;
import cn.kgc.springbootdemo.service.*;
import cn.kgc.springbootdemo.util.*;
import cn.kgc.springbootdemo.vo.UserLoginVO;
import cn.kgc.springbootdemo.vo.UserVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <h3>springbootdemo</h3>
 * <p>用户服务实现类</p>
 *
 * @author : DongZeJian
 * @date : 2020-04-28 10:43
 **/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private IRoleService service;

    /**
     * token过期时间,1小时
     */
    public static final int TOKEN_EXPIRE = 60 * 10;

    @Resource
    private TokenService tokenService;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private MinioTemplate minioTemplate;

    @Resource
    private TokenUtil tokenUtil;

    @Resource
    private UserRoleService userRoleService;

    @Resource
    private IUserLoginRecordService loginRecordService;

    @Autowired
    private RoleMenuService roleMenuService;

    /**
     * 校验用户账号是否存在
     *
     * @param userCode 用户账号
     * @return 结果
     */
    @Override
    public R<Boolean> checkUserCode(String userCode) {
        //TODO 1校验
        Validator.validateGeneral(userCode, "账号由：字母，数字，下划线，减号 4-16位组成");

        Validator.validateNull(findByUserCode(userCode), "账号已存在");


        return R.ok(true, "账号可用");
    }

    /**
     * 通过用户ID查询用户信息
     *
     * @param userID
     * @return
     */
    @Override
    public User findByUserID(String userID) {
        return baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getId, userID));
    }

    /**
     * 通过用户账号查询用户信息
     *
     * @param userCode
     * @return
     */
    @Override
    public User findByUserCode(String userCode) {
        return baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUserCode, userCode));
    }

    /**
     * 注册
     *
     * @param registerUserForm 注册对象表单
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean register(HttpServletRequest request, RegisterUserForm registerUserForm) {
        //校验验证码
        HttpSession session = request.getSession();
        String verifyCode = (String) session.getAttribute("VerifyCode");
        Validator.validateTrue(StrUtil.equalsIgnoreCase(registerUserForm.getVerifyCode(),verifyCode) , "验证码错误");
        session.removeAttribute("VerifyCode");

        //1.判断用户账号是否存在
        Validator.validateNull(findByUserCode(registerUserForm.getUserCode()), "账号已存在");
        //2.校验两次密码是否相等   如果前端传过来加密的 要先解密
        Validator.validateEqual(registerUserForm.getPassword(), registerUserForm.getVerifyPassword(), "两次输入的密码不相等");
        //校验身份证的性别 和 填写的性别是否一致
        int sex = IdcardUtil.getGenderByIdCard(registerUserForm.getIdCard());
        Validator.validateNotEqual(sex + 1, registerUserForm.getSex(), "性别输入错误");
        //实现新增  密码加密保存
        String id = GetTime.getGuid();

        baseMapper.insert(new User(
                id,
                registerUserForm.getUserCode(),
                SecureUtil.md5(registerUserForm.getPassword()),
                registerUserForm.getUserName(),
                registerUserForm.getSex(),
                registerUserForm.getPhone(),
                registerUserForm.getBirth(),
                null, null, null, registerUserForm.getIdCard()));
        //默认为注册账号 添加为普通用户
        UserRole userRole = new UserRole(id, registerUserForm.getRoleId());
        userRoleService.save(userRole);
        return true;
    }

    /**
     * 修改密码
     *
     * @param updateUserForm 修改密码表单对象
     * @return 结果
     */
    @Override
    public Boolean updateUserPassword(UpdateUserForm updateUserForm) {
        //判断用户账号是否存在
        User user = Validator.validateNotNull(findByUserCode(updateUserForm.getUserCode()), "用户账号不存在");
        //校验密码是否正确
        Validator.validateEqual(SecureUtil.md5(updateUserForm.getPassword()), user.getPasswrod(), "旧密码不正确");
        //校验两次输入的新密码是否一致
        Validator.validateEqual(updateUserForm.getNewPasswordVerify(), updateUserForm.getNewPassword(), "两次输入的新密码不相等");
        //校验新密码和旧密码是否相等   疑问点1
        Validator.validateEqual(updateUserForm.getNewPassword(), SecureUtil.md5(user.getPasswrod()), "新旧密码不能相同");
        //修改密码
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper.eq("user_Code", user.getUserCode());
        User newUser = new User();
        newUser.setPasswrod(SecureUtil.md5(updateUserForm.getNewPassword()));
        baseMapper.update(newUser, userUpdateWrapper);
        return true;
    }

    /**
     * 删除账号
     * @param id
     * @return 删除成功true  失败false
     */
    @Override
    public Boolean deleteUser(String id) {
        baseMapper.deleteById(id);
        return true;
    }

    /**
     * 分页用户查询
     * @param userPageForm 分页查询对象
     * @return 查询结果集合
     */
    @Override
    public IPage<UserVO> selectUsersByLimit(UserPageForm userPageForm) {
        Page<UserVO> userVOIPage = baseMapper.userPage(userPageForm.toPage(), userPageForm);
        List<UserVO> records = userVOIPage.getRecords();
        for (UserVO record : records) {
            record.setPhone(FieldSecurityInfoUtils.hidden(record.getPhone()));
            record.setIdCard(FieldSecurityInfoUtils.hidden(record.getIdCard()));
        }
        return userVOIPage;
    }

    /**
     *  查询用户登录信息
     *
     * @param id 用户Id
     * @return
     */
    @Override
    public UserLoginVO getUserLoginInfoById(String id) {
        //获得用户信息
        UserLoginVO userLoginVO=Validator.validateNotNull(baseMapper.getUserLoginInfoById(id),"用户不存在或者Id错误");
        userLoginVO.setPhone(FieldSecurityInfoUtils.hidden(userLoginVO.getPhone()));
        userLoginVO.setIdCard(FieldSecurityInfoUtils.hidden(userLoginVO.getIdCard()));

        userLoginVO.setMenus(baseMapper.getMenusByRoleId(userLoginVO.getRoleId()));
        return userLoginVO;
    }

    /**
     * 登录验证
     *
     * @param response
     * @param login    登录对象
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject login(HttpServletRequest request, HttpServletResponse response, LoginForm login) {
        JSONObject jsonObject = new JSONObject();


        //校验验证码
        HttpSession session = request.getSession();
        String verifyCode = (String) session.getAttribute("VerifyCode");
        Validator.validateTrue(StrUtil.equalsIgnoreCase(login.getVerifyCode(),verifyCode) , "验证码错误");
        //验证通过后 删除 此次验证码
        session.removeAttribute("VerifyCode");

        //TODO 1判断用户账号是否存在
        User user = Validator.validateNotNull(findByUserCode(login.getUserCode()), "用户账号不存在");
        //校验密码
        Validator.validateEqual(user.getPasswrod(), SecureUtil.md5(login.getPassword()), "密码不正确");

        UserRole userRole = userRoleService.getOne(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, user.getId()));


        //TODO 2生成token
        String token = tokenService.getToken(user);
        user.setIdCard(FieldSecurityInfoUtils.hidden(user.getIdCard()));
        user.setPhone(FieldSecurityInfoUtils.hidden(user.getPhone()));
        jsonObject.put("token", token);
        jsonObject.put("user", user);
        jsonObject.put("role", userRole.getRoleId());

        //TODO 3保存到Cookie
        Cookie cookie = new Cookie("token", token);
        cookie.setMaxAge(TOKEN_EXPIRE);
        cookie.setPath("/");
        response.addCookie(cookie);

        //TODO 4删除原有缓存
        Set<String> keys = redisUtil.keys("*" + user.getId() + "*");
        for (String key : keys) {
            redisUtil.del(key);
        }


        //TODO 5将对象保存到Redis
        redisUtil.set(CommonConstants.COOKIE_NAME_TOKEN + ":" + user.getId() + token, JSONUtil.toJsonStr(user), TOKEN_EXPIRE);

        //TODO 6生成登录记录
        UserLoginRecord record=new UserLoginRecord();
        record.setId(GetTime.getGuid())
                .setLoginTime(LocalDateTime.now())
                .setUserId(user.getId())
                .setLoginAddress(getAddressByIP(RequestIpUtils.getIpAddr()))
                .insert()
        ;
        return jsonObject;
    }

    /**
     *  根据ip地址获取详细的ip所在省市区地址信息
     *
     * @param ip
     * @return
     */
    public String getAddressByIP(String ip){
        //调用腾讯接口  可以是我自己的应用key 请求控制台管理
        String s = HttpRequest.get("https://apis.map.qq.com/ws/location/v1/ip?ip="+ip+"&key=UUGBZ-C6NWF-KM7JZ-JKUVR-WXBRE-ONBCY")
                .timeout(20000)
                .execute().body();
        Map map = com.alibaba.fastjson.JSONObject.parseObject(s, Map.class);
        if (MapUtil.getInt(map,"status")!=0){
            log.error("调用腾讯地址接口错误-->{}",MapUtil.getStr(map,"message"));
            return null;
        }
        Map o = (Map) map.get("result");
        Map o1 = (Map) o.get("ad_info");

        return MapUtil.getStr(o1,"nation")+MapUtil.getStr(o1,"province")+MapUtil.getStr(o1,"city")+MapUtil.getStr(o1,"district");
    }


    /**
     * 退出登录
     *
     * @return
     */
    @Override
    public Boolean signOut() {
        //todo 获取token
        String token = tokenUtil.getToken();
        Validator.validateNotEmpty(token, "token不能为空");

        //TODO 1删除Cookie
        Cookie cookie = new Cookie("token", null);
        cookie.setMaxAge(0);

        //TODO 2删除Redis token用户信息 作为黑名单
        User user = Validator.validateNotNull(tokenUtil.getUser(), "当前token已失效或者已注销");
        redisUtil.del(CommonConstants.COOKIE_NAME_TOKEN + ":" + user.getId() + token);

        return true;
    }


    /**
     * 身份证取年月日，目前只支持18位身份证
     *
     * @param idcard
     * @return
     */
    public LocalDate transferDateOfbirth(String idcard) {
        //取年
        String year = idcard.substring(6, 10);
        //取月
        String month = idcard.substring(10, 12);
        //取日
        String day = idcard.substring(12, 14);
        //拼装字符串的年月日
        String birthday = year + "-" + month + "-" + day;
        //日期转换
        DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");//定义一个时间转换格式“年-月-日”
        Date date = null;
        try {     //捕获类型转换（解析）异常
            date = fmt.parse(birthday);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }
}
